package com.qust.enDecrpytion.proxy.decryption;

import com.qust.enDecrpytion.annotations.Decryption;
import com.qust.enDecrpytion.annotations.EnableEnDecryption;
import com.qust.enDecrpytion.annotations.Encryption;
import com.qust.enDecrpytion.cache.DecryptionLru;
import com.qust.enDecrpytion.cache.EncryptionLru;
import com.qust.enDecrpytion.proxy.AbstractEnDecryptionProxy;
import com.qust.enDecrpytion.proxy.ChainCall;
import com.qust.enDecrpytion.proxy.encryption.EncryptionProxy;
import com.qust.enDecrpytion.proxy.parser.DefaultParser;
import com.qust.enDecrpytion.proxy.parser.Parser;
import com.qust.enDecrpytion.proxy.parser.ParserPOJO;
import com.qust.enDecrpytion.proxy.status.AbstractTypeHandle;
import com.qust.enDecrpytion.proxy.status.PoJoTypeHandle;
import com.qust.enDecrpytion.proxy.status.StringTypeHandle;
import com.qust.enDecrpytion.proxy.status.TypeHandleContext;
import com.qust.enDecrpytion.util.Verification;
import com.qust.encryptionAndDecryption.IEnDecryption;
import net.sf.cglib.proxy.MethodProxy;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

public class DecryptionProxy extends AbstractEnDecryptionProxy implements ChainCall {
    private final Logger logger = LogManager.getLogger(DecryptionProxy.class);
    private Object targetProxy;


    private TypeHandleContext context = new TypeHandleContext()
            .addHandle(new StringTypeHandle())
            .addHandle(new PoJoTypeHandle());

    private final EncryptionLru encryptionLru = EncryptionLru.getInstance(10);
    private final DecryptionLru decryptionLru = DecryptionLru.getInstance(10);

    private final Map<String, ParserPOJO> parserPOJOMap = new HashMap<>();
    private final Parser parser = new DefaultParser();

    public DecryptionProxy(Object target, IEnDecryption enDecryption) {
        this.targetClass = target.getClass();
        this.targetObject = target;
        this.enDecryption = enDecryption;
    }

    public void ConfigContext() {
        this.context.setEncryptionLru(encryptionLru);
        this.context.setDecryptionLru(decryptionLru);
    }

    public void ConfigContext(Class<? extends AbstractTypeHandle>[] handlers){
        this.ConfigContext();
        Arrays.stream(handlers).forEach(aClass -> {
            try {
                context.addHandle(aClass.newInstance());
            } catch (InstantiationException | IllegalAccessException e) {
                throw new RuntimeException(e);
            }
        });
    }


    @Override
    public Object call() {
        EncryptionProxy encryptionProxy = new EncryptionProxy(this.targetObject, this.enDecryption, this.context);
        return encryptionProxy.call();
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
//        if (method.isAnnotationPresent(EnableEnDecryption.class)) {
//            logger.info("创建解密层");
//            // 检验参数
//            logger.debug("检验参数");
//            this.verification(method);
//            // 参数解密
//            objects = this.parametersEnDecryption(method.getParameters(), objects);
//            // 执行加密方法
//            this.targetProxy = this.call();
//            Object result = method.invoke(targetProxy, objects);
//            // 返回值解密
//            return this.returnEnDecryption(method, result);
//        }
        if (method.isAnnotationPresent(EnableEnDecryption.class)){
            this.verification(method); // 验证

            String name = method.getName(); // 获取方法名作为存储解析元数据的key
            if (!parserPOJOMap.containsKey(name)){ // 解析注解
                ParserPOJO parser1 = parser.parser(method, Decryption.class);
                parserPOJOMap.put(name,parser1);
            }

            ParserPOJO parserPOJO = parserPOJOMap.get(name);
            ArrayList<Integer> parameters = parserPOJO.getParameters(); // 参数
            parameters.forEach(integer -> objects[integer] = context.handle(objects[integer],false,Decryption.class, s -> enDecryption.decryption(s)));

            if (this.targetProxy == null){
                this.targetProxy = this.call(); // 加密操作
            }
            Object result = method.invoke(targetProxy, objects); // 执行方法

            return parserPOJO.isReturnType()?context.handle(result, false,Decryption.class, s -> enDecryption.decryption(s)):result; // 返回值
        }

        return method.invoke(targetObject, objects);
    }

    private void verification(Method method) {
        Verification.methodAnnotationDuplicateVerification(method, Decryption.class, Encryption.class);
        Verification.parametersAnnotationDuplicateVerification(method, Decryption.class, Encryption.class);
    }

}
